<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.1.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/blog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/blog/css/main.css">


<link rel="stylesheet" href="/blog/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"coderiding.gitee.io","root":"/blog/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="一般来讲，一个线程一次只能执行一个任务，执行完成后线程就会退出。如果我们需要一个机制，让线程随时处理事件并不退出，那么，能想到的可能就是死循环。当然，它肯定不仅仅是一个死循环那么简单，它要考虑如何管理事件，如何让线程在没有消息时休眠以避免占用资源，又如何在消息到来时被唤醒。而综上这个机制，就叫做『事件循环』机制，当然，在各个系统中可能叫法不一，但意思相似。而iOS中，它就叫做RunLoop。">
<meta property="og:type" content="article">
<meta property="og:title" content="如何理解iOS的Runloop？">
<meta property="og:url" content="https://coderiding.gitee.io/blog/2020/09/15/%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3iOS%E7%9A%84Runloop%EF%BC%9F/index.html">
<meta property="og:site_name" content="汶的博客">
<meta property="og:description" content="一般来讲，一个线程一次只能执行一个任务，执行完成后线程就会退出。如果我们需要一个机制，让线程随时处理事件并不退出，那么，能想到的可能就是死循环。当然，它肯定不仅仅是一个死循环那么简单，它要考虑如何管理事件，如何让线程在没有消息时休眠以避免占用资源，又如何在消息到来时被唤醒。而综上这个机制，就叫做『事件循环』机制，当然，在各个系统中可能叫法不一，但意思相似。而iOS中，它就叫做RunLoop。">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-09-15T01:04:13.000Z">
<meta property="article:modified_time" content="2020-09-15T01:13:42.744Z">
<meta property="article:author" content="汶">
<meta property="article:tag" content="底层">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://coderiding.gitee.io/blog/2020/09/15/%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3iOS%E7%9A%84Runloop%EF%BC%9F/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>如何理解iOS的Runloop？ | 汶的博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/blog/atom.xml" title="汶的博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/blog/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">汶的博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/blog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/blog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://coderiding.gitee.io/blog/2020/09/15/%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3iOS%E7%9A%84Runloop%EF%BC%9F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/side_image.png">
      <meta itemprop="name" content="汶">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汶的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          如何理解iOS的Runloop？
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2020-09-15 09:04:13 / 修改时间：09:13:42" itemprop="dateCreated datePublished" datetime="2020-09-15T09:04:13+08:00">2020-09-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/%E6%94%B9%E5%8F%98%E4%B8%96%E7%95%8C/" itemprop="url" rel="index"><span itemprop="name">改变世界</span></a>
                </span>
            </span>

          
            <div class="post-description">一般来讲，一个线程一次只能执行一个任务，执行完成后线程就会退出。如果我们需要一个机制，让线程随时处理事件并不退出，那么，能想到的可能就是死循环。当然，它肯定不仅仅是一个死循环那么简单，它要考虑如何管理事件，如何让线程在没有消息时休眠以避免占用资源，又如何在消息到来时被唤醒。而综上这个机制，就叫做『事件循环』机制，当然，在各个系统中可能叫法不一，但意思相似。而iOS中，它就叫做RunLoop。</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h2 id="Runloop存在的价值"><a href="#Runloop存在的价值" class="headerlink" title="Runloop存在的价值"></a>Runloop存在的价值</h2><ul>
<li><ol>
<li>我们思考一下：程序为什么可以处理用户的各种事件，而且可以保证程序不退出？答案：就是因为程序里面有个系统的默认运行循环，所以能保证程序不退出，并且处理用户的各种事件 </li>
</ol>
</li>
</ul>
<hr>
<ul>
<li><ol start="2">
<li>想要了解为什么要使用运行循环，我们可以从使用运行循环的目的来了解，使用了运行循环我们可以： <ul>
<li>a. 保证程序不退出 ; </li>
<li>b. 负责处理输入事件 ; </li>
<li>c. 如果没有事件发生，会让程序进入休眠状态。 </li>
</ul>
</li>
</ol>
</li>
<li><p>从上面可以得出结论，为什么要使用运行循环，因为这是一个 APP 的基本，没有运行循环，就没有 APP 的正常运行。 </p>
</li>
</ul>
<hr>
<ul>
<li><ol start="3">
<li>通过上面的了解我们可以总结运行循环的优点：有事情就做事情，没事情就休息；优点：节省 CPU 资源、提高程序性能；</li>
</ol>
</li>
</ul>
<h3 id="Runloop与线程"><a href="#Runloop与线程" class="headerlink" title="Runloop与线程"></a>Runloop与线程</h3><ul>
<li><ol>
<li>每一个线程内部都有一个消息循环。只有主线程的消息循环默认开启 , 子线程的消息循环默认不开启，一个运行循环对应着一条唯一的线程，如何让子线程不死 ，给这条子线程开启一个运行循环，子线程的 runloop 需要手动创建 , 需要手动开启 </li>
</ol>
</li>
<li><ol start="2">
<li>线程在执行中的休眠和激活就是由 RunLoop 对象进行管理的 </li>
</ol>
</li>
<li>3.RunLoop 是用来管理线程的 </li>
<li><ol start="4">
<li>每一个线程都有一个 RunLoop 对象。可以通过具体的方法去获得 </li>
</ol>
</li>
<li><ol start="5">
<li>但是需要注意：虽然每一个线程都可以获取 RunLoop 对象，但是并不是每一个线程中都有实例对象，我们可以这样理解：如果我们不获取 RunLoop ，这个 RunLoop 就不存在，我们获取时，如果不存在，就会去创建。在主线程中，这个 MainRunLoop 是默认创建并运行激活的 </li>
</ol>
</li>
<li><ol start="6">
<li>每条线程都有唯一的一个与之对应的 RunLoop 对象</li>
</ol>
</li>
<li>7.判断runloop是否有效的关键是有没有事件源或者定时源</li>
</ul>
<h2 id="Runloop的工作原理"><a href="#Runloop的工作原理" class="headerlink" title="Runloop的工作原理"></a>Runloop的工作原理</h2><h3 id="认识Runloop"><a href="#认识Runloop" class="headerlink" title="认识Runloop"></a>认识Runloop</h3><ul>
<li>NSRunloop </li>
<li>CFRunLoopRef </li>
<li>两种 API 的区别 1 ： NSRunloop 线程不安全， CFRunLoopRef 线程安全； NSRunLoop 是 Cocoa 框架中的类，与之对应的是在 Core Fundation 中有一个 CFRunLoopRef 类。这两者的区别是前者不是线程安全的，而 CFRunLoopRef 是线程安全的。所以： NSRunloop 不能调用其他线程的方法 . </li>
<li>两种 API 的区别 2 ： CFRunLoopRef 创建一个 timer 必须添加到 runloop 才会执行 , 添加的时候要指定模式 defaurce 模式 , 不对程序做任何操作 timer 就会后台运行 , 当我进行操作的时候 runloop 模式就会从默认模式切换到其他模式 , 假如说我操作 scrollerView 它就会从 default 模式切换到 tracking 模式。而 roonloop 同一时刻只能执行一种模式 . </li>
<li>NSRunLoop 是基于 CFRunLoopRef 的一层 OC 包装，提供了面向对象的 API ，但是这些 API 不是线程安全的。所以要了解 RunLoop 内部结构，需要多研究 CFRunLoopRef 层面的 API （ Core Foundation 层面） </li>
</ul>
<hr>
<ul>
<li>RunLoop 类 五个类 <ul>
<li>CFRunLoopRef </li>
<li>CFRunLoopModeRef     // 模式 </li>
<li>CFRunLoopSourceRef   // 事件源 </li>
<li>CFRunLoopTimerRef    // 时间源 </li>
<li>CFRunLoopObserverRef // 观察者</li>
</ul>
</li>
</ul>
<h3 id="运行循环本质"><a href="#运行循环本质" class="headerlink" title="运行循环本质"></a>运行循环本质</h3><ul>
<li>从字面上看，就是运行循环跑圈 </li>
<li>其实它内部就是 do-while 循环，在这个循环内部不断地处理各种任务（比如 Source 、 Timer 、 Observer ） </li>
<li>循环体的开始需要检测是否有需要处理的事件，如果有则去处理，如果没有则进入睡眠以节省 CPU 时间 </li>
<li>什么是输入源？ </li>
<li>什么是模式？ </li>
<li>什么是观察者 </li>
<li>我们思考一下：程序为什么可以处理用户的各种事件，而且可以保证程序不退出？答案：就是因为程序里面有个系统的默认运行循环，所以能保证程序不退出，并且处理用户的各种事件 </li>
<li>想要了解为什么要使用运行循环，我们可以从使用运行循环的目的来了解，使用了运行循环我们可以： </li>
</ul>
<hr>
<ul>
<li>a. 保证程序不退出 ; </li>
<li>b. 负责处理输入事件 ; </li>
<li>c. 如果没有事件发生，会让程序进入休眠状态。 </li>
<li>从上面可以得出结论，为什么要使用运行循环，因为这是一个 APP 的基本，没有运行循环，就没有 APP 的正常运行。 </li>
<li>通过上面的了解我们可以总结运行循环的优点：有事情就做事情，没事情就休息；优点：节省 CPU 资源、提高程序性能； </li>
<li>系统默认就是一个运行循环，可以保证程序不死掉；系统默认运行循环在主线程 </li>
<li>每一个线程内部都有一个消息循环。只有主线程的消息循环默认开启 , 子线程的消息循环默认不开启，一个运行循环对应着一条唯一的线程，如何让子线程不死，给这条子线程开启一个运行循环，子线程的 runloop 需要手动创建 , 需要手动开启 </li>
<li>线程在执行中的休眠和激活就是由 RunLoop 对象进行管理的 </li>
<li>RunLoop 是用来管理线程的 </li>
<li>每一个线程都有一个 RunLoop 对象。可以通过具体的方法去获得 </li>
<li>但是需要注意：虽然每一个线程都可以获取 RunLoop 对象，但是并不是每一个线程中都有实例对象，我们可以这样理解：如果我们不获取 RunLoop ，这个 RunLoop 就不存在，我们获取时，如果不存在，就会去创建。在主线程中，这个 MainRunLoop 是默认创建并运行激活的 </li>
<li>每条线程都有唯一的一个与之对应的 RunLoop 对象 </li>
<li>运行循环的生命周期：在第一次获取时创建，在线程结束时销毁 </li>
<li>每次运行一个 RunLoop ，你指定（显式或隐式） RunLoop 的运行模式。当相应的模式传递给 RunLoop 时，只有与该模式对应的 Input Source 才被监控并允许 RunLoop 对事件进行处理（与此类似，也只有与该模式对应的 Observers 才会被通知）</li>
</ul>
<hr>
<ul>
<li>runloop 就像是事件驱动，有事情做就唤醒，没有事做就睡眠 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">int main(int argc, char * argv[]) &#123; </span><br><span class="line">	while (AppIsRunning) &#123; </span><br><span class="line">		id whoWakesMe &#x3D; SleepForWakingUp(); </span><br><span class="line">		id event &#x3D; GetEvent(whoWakesMe); </span><br><span class="line">		HandleEvent(event); </span><br><span class="line">	&#125; </span><br><span class="line">	return 0; </span><br><span class="line">&#125; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>使程序一直运行并接受用户输入 </li>
<li>决定程序在何时应该处理哪些 Event </li>
<li>调用解耦（ Message Queue ） </li>
<li>节省 CPU 时间</li>
</ul>
<h3 id="Runloop工作流程"><a href="#Runloop工作流程" class="headerlink" title="Runloop工作流程"></a>Runloop工作流程</h3><ul>
<li>创建消息（即输入源）； </li>
<li>指定该事件（源）在循环中运行的模式，并加入循环； </li>
<li>当事件的模式与消息循环的模式匹配的时候，消息才会运行。 </li>
<li>运行逻辑总结：一个线程对应一个 runLoop, 主线程的 runloop 是程序一启动 , 默认就创建一个 runloop, 创建好了之后就会给它添加一些默认的模式 , 每个模式里面会有很多的 source /timer/observer , 添加好这些模式后 ,observer 就会监听主线程的 runloop, 进入 runloop 后 , 就开始处理事件 , 先处理 timer, 再处理 source0,source0 处理完之后再处理 source1, 当把这些所有的事件反复的处理完之后 , 如果没有事件了 , 那么 runloop 就会进入睡眠状态 , 当用户又触发了新的事件 , 就会唤醒 runloop, 唤醒 runloop 后回到第二步 , 重新处理新的 timer, 新的 source0, 新的 source1, 处理完后就睡眠 , 一直反复 , 当我们把程序关闭或者强退 , 这个时候 observer 就会监听都 runloop 退出了 .</li>
</ul>
<h3 id="输入源（mx：消息、时间-要做的事）"><a href="#输入源（mx：消息、时间-要做的事）" class="headerlink" title="输入源（mx：消息、时间-要做的事）"></a>输入源（mx：消息、时间-要做的事）</h3><ul>
<li>1.RunLoop 能处理的事件：输入源（事件源）、定时源 ; <ul>
<li>输入源也就是用户的各种事件，如触摸事件、定时器事件 NSTimer 、选择器事件 selector; </li>
<li>输入源是为了接收消息；当为一个长期存活的现场配置 runloop 时，至少添加一个 input source 去接收消息 </li>
<li>CFRunLoopSourceRef 是事件产生的地方 </li>
</ul>
</li>
</ul>
<hr>
<ul>
<li>2.输入源（事件源） <ul>
<li>1 ）一般用来处理异步事件的；</li>
<li>2 ）两种分法 <ul>
<li>旧：按照苹果官方文档进行划分的 </li>
<li>新：基于函数的调用栈来进行划分的（ source0 和 source1 ） </li>
</ul>
</li>
<li>3 ）旧：按照苹果官方文档进行划分的 <ul>
<li>Cocoa Perform Selector Sources （ performSelector 源） </li>
<li>Custom Input Sources （自定义源） </li>
<li>Port-Based Sources （基于端口 Mach port 的源）通过内核和其他线程通信，接收、分发系统事件 </li>
</ul>
</li>
<li><ol start="4">
<li>新：基于函数的调用栈来进行划分的（ source0 和 source1 ） <ul>
<li>source0 是非基于端口的 , 是用户自己手动触发的操作 , 比如触摸滑动等操作 . 只包含了一个回调（函数指针），它并不能主动触发事件。使用时，你需要先调用 CFRunLoopSourceSignal(source) ，将这个 Source 标记为待处理，然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop ，让其处理这个事件。 </li>
<li>source1 是系统内部的一些端口触发的事件 </li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<ul>
<li><p>3.定时源 </p>
<ul>
<li>1 ）一般用来处理同步事件的 </li>
<li>2 ）定时源即 NSTimer （ add timer ） </li>
<li><ol start="3">
<li>CFRunLoopTimerRef 是基于时间的触发器 </li>
</ol>
</li>
<li><ol start="4">
<li>CFRunLoopTimerRef 基本上说的就是 NSTimer ，它受 RunLoop 的 Mode 影响 </li>
</ol>
</li>
<li><ol start="5">
<li>GCD 的定时器不受 RunLoop 的 Mode 影响 </li>
</ol>
</li>
<li>6 ）一个 mode 里面可以添加多个 NSTimer ，也就是说以后当创建 NSTimer 的时候，可以指定它是在什么模式下运行的。 </li>
</ul>
</li>
<li><p>默认的运行循环模式，当有触发事件发生时，一般时钟会暂时停止。这是大部分的运行循环模式所采用的 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSDefaultRunLoopMode]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>
</li>
<li><p>NSRunLoopCommonModes 这个模式是 “ 监听滚动模式 ” ，触摸屏幕拖拽也不会管你，还是继续执行时钟 </p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];</span><br><span class="line">	</span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li>4.performSelector 源 <ul>
<li><ol>
<li>当调用 NSObject 的 performSelecter:afterDelay: 后，实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop ，则这个方法会失效。 </li>
</ol>
</li>
<li><ol start="2">
<li>当调用 performSelector:onThread: 时，实际上其会创建一个 Timer 加到对应的线程去，同样的，如果对应线程没有 RunLoop 该方法也会失效。 </li>
</ol>
</li>
<li><ol start="3">
<li>performSelector 应用：可以让某些事件（行为、任务）在特定模式下执行 ; 有时候图片比较大，渲染到屏幕耗费时间，会造成界面卡顿，可以让图片在 UIScrollView 滚动完之后执行 </li>
</ol>
</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@&quot;placeholder&quot;] afterDelay:2 inModes:@[NSDefaultRunLoopMode]]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li><ol start="5">
<li>自定义源 </li>
</ol>
</li>
</ul>
<hr>
<ul>
<li><ol start="6">
<li>基于端口源</li>
</ol>
</li>
</ul>
<h3 id="mode模式（mx：拿到源就给他配置一个模式，是如何工作的模式）"><a href="#mode模式（mx：拿到源就给他配置一个模式，是如何工作的模式）" class="headerlink" title="mode模式（mx：拿到源就给他配置一个模式，是如何工作的模式）"></a>mode模式（mx：拿到源就给他配置一个模式，是如何工作的模式）</h3><ul>
<li>RunLoopMode 是一个集合，包括监听：事件源，定时器，以及需通知的 RunLoop observers </li>
<li>CFRunloopModeRef 代表着 Runloop 的运行模式 </li>
<li>一个 Runloop 中可以有多个 mode, 一个 mode 里面又可以有多个 source\observer\timer 等等 </li>
<li>每次 runloop 启动的时候，只能指定一个 mode, 这个 mode 被称为该 Runloop 的当前 mode </li>
<li>如果需要切换 mode, 只能先退出当前 Runloop, 再重新指定一个 mode 进入； RunLoop 在某一时刻只能在一种模式下运行 , 更换模式时需要暂停当前的 Loop, 然后重启新的 Loop. </li>
<li>这样做主要是为了分割不同组的定时器等，让他们相互之间不受影响 </li>
</ul>
<ul>
<li>mode的使用原则<ul>
<li><ol>
<li>同一时间段只存在一种mode</li>
</ol>
</li>
<li><ol start="2">
<li>切换 Mode 的原则：停止当前 Loop ，重启新 Loop</li>
</ol>
</li>
</ul>
</li>
</ul>
<h4 id="mode的种类："><a href="#mode的种类：" class="headerlink" title="mode的种类："></a>mode的种类：</h4><ul>
<li>NSDefaultRunLoopMode ：默认状态、空闲状态</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&#x2F;&#x2F;NSDefaultRunLoopMode 使用 </span><br><span class="line">[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li><p>UITrackingRunLoopMode ：滑动 ScrollView 时</p>
</li>
<li><p>UIInitializationRunLoopMode ：私有， App启动时</p>
</li>
<li><p>NSRunLoopCommonModes ： Mode 集合</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&#x2F;&#x2F; 若不希望 Timer 被 ScrollView 影响，需添加到 NSRunLoopCommonModes </span><br><span class="line">NSTimer *timer &#x3D; [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];</span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li>NS 开头 (Cocoa) <ul>
<li>NSDefaultRunLoopMode 默认模式（最常用的循环模式） 默认 Mode, 通常主线程在这个模式下运行处理 modal panels （ The mode to deal with input sources other than NSConnection objects. This is the most commonly used run-loop mode. Available in iOS 2.0 and later. ） ,DefaultMode 是 App 平时所处的状态 </li>
<li>NSEventTrackingRunLoopMode 此模式下用于处理窗口事件 , 鼠标事件等 , 如组件拖动输入源 UITrackingRunLoopModes 不处理定时事件； TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态 </li>
<li>NSRunLoopCommonModes 普通模式（一组模式的集合 , 包括 NSDefaultRunLoopMode 和 UITrackingRunLoopMode ）（ Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common” modes; see the description of CFRunLoopAddCommonMode for details. ）；哪些 mode 默认久标记为 “Common” 属性：主线程的 RunLoop 里有两个预置的 Mode ： kCFRunLoopDefaultMode 和 UITrackingRunLoopMode 。这两个 Mode 都已经被标记为 “Common” 属性。 此模式用于配置 ” 组模式 ” ，一个输入源与此模式关联，则输入源与组中的所有模式相关联。这是一组可配置的通用模式。将 input sources 与该模式关联则同时也将 input sources 与该组中的其它模式进行了关联。 </li>
<li>NSConnectionReplyMode 此模式用于处理 NSConnection 的回调事件 , ：处理 NSConnection 事件，属于系统内部，用户基本不用 </li>
<li>NSModalPanelRunLoopMode 模态模式，此模式下， RunLoop 只对处理模态相关事件 </li>
</ul>
</li>
</ul>
<hr>
<ul>
<li>CF 开头 (Core Foundation)1 ）系统默认注册了 5 中 mode <ul>
<li>kCFRunLoopDefaultMode App 的默认 Mode ，通常主线程是在这个 Mode 下运行；缺省情况下，将包含所有操作，并且大多数情况下都会使用此模式 , 几乎包括所有输入源 ( 除 NSConnection), 对应 NSDefaultRunLoopMode </li>
<li>UITrackingRunLoopMode 界面跟踪 Mode ，用于 ScrollView 追踪触摸滑动，保证界面滑动时不受其他 Mode 影响 </li>
<li>UIInitializationRunLoopMode 在刚启动 App 时第进入的第一个 Mode ，启动完成后就不再使用 </li>
<li>GSEventReceiveRunLoopMode 接受系统事件的内部 Mode ，通常用不到 </li>
<li>kCFRunLoopCommonModes 这是一个占位用的 Mode ，不是一种真正的 Mode </li>
</ul>
</li>
</ul>
<hr>
<h4 id="mode切换"><a href="#mode切换" class="headerlink" title="mode切换"></a>mode切换</h4><ul>
<li><p>模式切换1 ： NSDefaultRunLoopMode 切换为 NSEventTrackingRunLoopMode </p>
<ul>
<li>当你创建一个 Timer 并加到 DefaultMode 时， Timer 会得到重复回调，但此时滑动一个 TableView 时， RunLoop 会将 mode 切换为 TrackingRunLoopMode ，这时 Timer 就不会被回调，并且也不会影响到滑动操作。 </li>
</ul>
<ul>
<li>当设置事件模式为 NSDefaultRunLoopMode 时，拖动 UITextView 界面 , 定时源停止运行；当停止拖动，定时源又继续运行 ; 当设置事件模式为 NSRunLoopCommonModes 时，拖动 UITextView 界面 , 定时源持续运行不受影响。此外，当设置事件模式为 NSRunLoopCommonModes 时，未拖动 UITextView 界面时，消息循环的模式为 kCFRunLoopDefaultMode ，当拖动 UITextView 界面时，消息循环的模式自动变为 UITrackingRunLoopMode </li>
</ul>
</li>
</ul>
<hr>
<ul>
<li><p>模式切换例2 </p>
<ul>
<li>调用了 scheduledTimer 返回的定时器，已经自动被添加到当前 runLoop 中，而且是 NSDefaultRunLoopMode</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">NSTimer *timer &#x3D; [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; </span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>当你在滑动 ScrollView 的时候上面的 timer 会失效 , 原因是 Timer 是默认加在 NSDefalutRunLoopMode 上的 , 而滑动 ScrollView 后系统把 RunLoop 切换为 UITrackingRunLoopMode, 所以 timer 就不会执行了 . 解决方法是把该 Timer 加到 NSRunLoopCommonModes 下 , 这样即使滑动 ScrollView 也不会影响 timer 了 . </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li>模式切换例3 <ul>
<li>另外还有一个 trick 是当 tableview 的 cell 从网络异步加载图片 , 加载完成后在主线程刷新显示图片 , 这时滑动 tableview 会造成卡顿 . 通常的思路是 tableview 滑动的时候延迟加载图片 , 等停止滑动时再显示图片 . 这里我们可以通过 RunLoop 来实现 .</li>
</ul>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[self.cellImageView performSelector:@sector(setImage:)withObject:downloadedImageafterDelay:0inModes:@[NSDefaultRunLoopMode]];</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>当 NSRunLoop 为 NSDefaultRunLoopMode 的时候 tableview 肯定停止滑动了 , why? 因为如果还在滑动中 , RunLoop 的 mode 应该是 UITrackingRunLoopMode. </li>
</ul>
<hr>
<h4 id="让一个事件同时在两个-mode-都有回调方法"><a href="#让一个事件同时在两个-mode-都有回调方法" class="headerlink" title="让一个事件同时在两个 mode 都有回调方法"></a>让一个事件同时在两个 mode 都有回调方法</h4><ul>
<li>有时你需要一个 Timer ，在两个 Mode 中都能得到回调，一种办法就是将这个 Timer 分别加入这两个 Mode 。 </li>
<li>还有一种方式，就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。 “commonModeItems” 被 RunLoop 自动更新到所有具有 “Common” 属性的 Mode 里去 </li>
<li>方式 1: </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">NSTimer *timer &#x3D; [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>定时器只运行在 NSDefaultRunLoopMode 下，一旦 RunLoop 进入其他模式，这个定时器就不会工作 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>定时器只运行在 UITrackingRunLoopMode 下，一旦 RunLoop 进入其他模式，这个定时器就不会工作 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li>方式 2 ： </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&#x2F;&#x2F; 定时器会跑在标记为 common modes 的模式下 &#x2F;&#x2F; 标记为 common modes 的模式： </span><br><span class="line"></span><br><span class="line">UITrackingRunLoopMode 和 NSDefaultRunLoopMode </span><br><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop观察者"><a href="#runloop观察者" class="headerlink" title="runloop观察者"></a>runloop观察者</h3><h4 id="RunLoop-观察者介绍"><a href="#RunLoop-观察者介绍" class="headerlink" title="RunLoop 观察者介绍"></a>RunLoop 观察者介绍</h4><ul>
<li>CFRunLoopObserverRef 是观察者，能够监听 RunLoop 的状态改变 </li>
<li>Observer 是监听 RunLoop 状态的， CoreFunction 向线程添加 runloop observers 来监听事件，意在监听事件发生时来做处理。 </li>
<li>线程除了处理输入源， RunLoop 也会生成关于 Run Loop 行为的通知（ notification ）。 RunLoop 观察者（ Run-Loop Observers ）可以收到这些通知，并在线程上面使用他们来作额外的处理；如果 RunLoop 没有任何源需要监视的话，它会在你启动之际立马退出。 </li>
</ul>
<h4 id="观察者状态"><a href="#观察者状态" class="headerlink" title="观察者状态"></a>观察者状态</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) &#123; </span><br><span class="line">	kCFRunLoopEntry &#x3D; (1UL &lt;&lt; 0),         &#x2F;&#x2F; 即将进入 Runloop </span><br><span class="line">	kCFRunLoopBeforeTimers &#x3D; (1UL &lt;&lt; 1),  &#x2F;&#x2F; 即将处理 NSTimer </span><br><span class="line">	kCFRunLoopBeforeSources &#x3D; (1UL &lt;&lt; 2), &#x2F;&#x2F; 即将处理 Sources </span><br><span class="line">	kCFRunLoopBeforeWaiting &#x3D; (1UL &lt;&lt; 5), &#x2F;&#x2F; 即将进入休眠 </span><br><span class="line">	kCFRunLoopAfterWaiting &#x3D; (1UL &lt;&lt; 6),  &#x2F;&#x2F; 刚从休眠中唤醒 </span><br><span class="line">	kCFRunLoopExit &#x3D; (1UL &lt;&lt; 7),          &#x2F;&#x2F; 即将退出 runloop </span><br><span class="line">	kCFRunLoopAllActivities &#x3D; 0x0FFFFFFFU &#x2F;&#x2F; 所有状态改变 </span><br><span class="line">&#125;; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>第一个参数 : 用于分配该 observer 对象的内存 CFAllocatorGetDefault </li>
<li>第二个参数 : 用以设置该 observer 所要关注的的事件 kCFRunLoopAllActivities </li>
<li>第三个参数 : 用于标识该 observer 是在第一次进入 run loop 时执行 , 还是每次进入 run loop 处理时均执行 YES </li>
<li>第四个参数 : 用于设置该 observer 的优先级 0 </li>
<li>第五个参数 : observer 监听到事件时的回调 block </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRunLoopObserverRef observer &#x3D; CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) &#123; </span><br><span class="line"></span><br><span class="line">    switch(activity)&#123; </span><br><span class="line">        case kCFRunLoopEntry: </span><br><span class="line">             NSLog(@&quot; 即将进入 loop&quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        case kCFRunLoopBeforeTimers: </span><br><span class="line">             NSLog(@&quot; 即将处理 timers&quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        case kCFRunLoopBeforeSources: </span><br><span class="line">            NSLog(@&quot; 即将处理 sources&quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        case kCFRunLoopBeforeWaiting: </span><br><span class="line">             NSLog(@&quot; 即将进入休眠 &quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        case kCFRunLoopAfterWaiting: </span><br><span class="line">             NSLog(@&quot; 刚从休眠中唤醒 &quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        case kCFRunLoopExit: </span><br><span class="line">             NSLog(@&quot; 即将退出 loop&quot;); </span><br><span class="line">        break; </span><br><span class="line"></span><br><span class="line">        default: </span><br><span class="line">        break; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;); </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<h4 id="参数解析"><a href="#参数解析" class="headerlink" title="参数解析"></a>参数解析</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode); </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>第一个参数 : 给哪个 RunLoop 添加监听 </li>
<li>第二个参数 : 需要添加的 Observer 对象 </li>
<li>第三个参数 : 在哪种模式下监听 </li>
</ul>
<hr>
<h4 id="释放观察者"><a href="#释放观察者" class="headerlink" title="释放观察者"></a>释放观察者</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRelease(observer); </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<h4 id="添加观察者过程"><a href="#添加观察者过程" class="headerlink" title="添加观察者过程"></a>添加观察者过程</h4><ul>
<li>1 ）创建一个观察者 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&#x2F;&#x2F; 创建一个 runloop 监听者 </span><br><span class="line">CFRunLoopObserverRef observer &#x3D; CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities,YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) &#123; </span><br><span class="line">NSLog(@” 监听 RunLoop 状态改变 —%zd”,activity); </span><br><span class="line">&#125;); </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>2 ）添加创建的观察者 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer,kCFRunLoopDefaultMode);&#x2F;&#x2F; 添加观察者：监听 RunLoop 的状态 </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>3 ）释放创建的观察者 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRelease(observer);&#x2F;&#x2F; 释放</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop启动的三种方式："><a href="#runloop启动的三种方式：" class="headerlink" title="runloop启动的三种方式："></a>runloop启动的三种方式：</h3><ul>
<li><ol>
<li>无条件的 —- 无条件的进入 Runloop 是最简单的方法，但也最不推荐使用的。因为这样会使你的线程处在一个永久的循环中，这会让你对 Runloop 本身的控制很少。你可以添加或删除输入源和定时器，但是退出 Runloop 的唯一方法是杀死它。没有任何办法可以让这 Runloop 运行在自定义模式下。 </li>
</ol>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] run]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li><ol start="2">
<li>设置超时时间 —- 替代无条件进入 Runloop 更好的办法是用预设超时时间来运行 Runloop ，这样 Runloop 运作直到某一事件到达或者规定的时间已经到期。如果是事件到达，消息会被传递给相应的处理程序来处理，然后 Runloop 退出。你可以重新启动 Runloop 来等待下一事件。如果是规定时间到期了，你只需简单的重启 Runloop 或使用此段时间来做任何的其他工作。 2 秒钟之后结束 </li>
</ol>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li><ol start="3">
<li>特定的模式 —- 除了超时机制，你也可以使用特定的模式来运行你的 Runloop 。模式和超时不是互斥的，他们可以在启动 RunLoop 的时候同时使用。模式限制了可以传递事件给 Runloop 的输入源的类型。暂停当前处理的流程，转而处理其他输入源，当 date 设置为 <a href="%E5%B0%86%E6%9D%A5%EF%BC%8C%E5%9F%BA%E6%9C%AC%E4%B8%8D%E4%BC%9A%E5%88%B0%E8%BE%BE%E7%9A%84%E6%97%B6%E9%97%B4">NSDate distantFuture</a> ，所以除非处理其他输入源结束，否则永不退出处理暂停的当前处理的流程 </li>
</ol>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="结束的三种方式："><a href="#结束的三种方式：" class="headerlink" title="结束的三种方式："></a>结束的三种方式：</h3><ul>
<li>给 RunLoop 设置超时时间 </li>
<li>通知 RunLoop 停止 —- 如果可以配置的话，推荐使用第一种方法。指定一个超时时间可以使 RunLoop 退出前完成所有正常操作，包括发送消息给 RunLoop 观察者。 </li>
<li>使用 CFRunLoopStop 来显式的停止 RunLoop 和使用超时时间产生的结果相似。 RunLoop 把所有剩余的通知发送出去再退出。与设置超时的不同的是你可以在无条件启动的 RunLoop 里面使用该技术。 </li>
</ul>
<hr>
<ul>
<li>运行循环 移除输入源 <ul>
<li>输入源被注册到 RunLoop 中时会有方法进行 remove 。但是定时器没有 remove ，但是它的 invalidate 方法可以将其从 RunLoop 中移除。 invalidate 是重要的也是唯一的将定时器从 RunLoop 中注销的方法，所以如果我们创建了定时器，就一定要再不适用的时候调用 invalidate 方法。 </li>
</ul>
</li>
</ul>
<hr>
<ul>
<li>自动释放池 , 什么时候创建和释放 ? <ul>
<li>(1) 第一次创建 : 是在 runloop 进入的时候创建 对应的状态 = KCFRunLoopEntry </li>
<li>(2) 最后一个退出 , 是在 runloop 退出的时候 对应的状态 = KCFRunLoopExit </li>
<li>(3) 其他的创建和释放 </li>
<li>每次睡觉的时候会释放前自动释放池 , 再创建一个新的 </li>
<li>即将进入睡眠的时候 , 先释放上一次创建的自动释放池 , 然后再创建一个新的释放池</li>
</ul>
</li>
</ul>
<h2 id="runloop使用的例子"><a href="#runloop使用的例子" class="headerlink" title="runloop使用的例子"></a>runloop使用的例子</h2><h3 id="runloop解决cell滑动timer停止"><a href="#runloop解决cell滑动timer停止" class="headerlink" title="runloop解决cell滑动timer停止"></a>runloop解决cell滑动timer停止</h3><ul>
<li>可以通过将timer添加到NSRunLoopCommonModes（kCFRunLoopCommonModes）来解决</li>
<li>将timer添加到NSDefaultRunLoopMode中</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">[NSTimer scheduledTimerWithTimeInterval:1.0</span><br><span class="line">     target:self</span><br><span class="line">     selector:@selector(timerTick:)</span><br><span class="line">     userInfo:nil</span><br><span class="line">     repeats:YES];</span><br><span class="line">&#x2F;&#x2F;然后再添加到NSRunLoopCommonModes里</span><br><span class="line">NSTimer *timer &#x3D; [NSTimer timerWithTimeInterval:1.0</span><br><span class="line">     target:self</span><br><span class="line">     selector:@selector(timerTick:)</span><br><span class="line">     userInfo:nil</span><br><span class="line">     repeats:YES];</span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop让crash的app起死回生"><a href="#runloop让crash的app起死回生" class="headerlink" title="runloop让crash的app起死回生"></a>runloop让crash的app起死回生</h3><ul>
<li>接到 Crash 的 Signal 后手动重启 RunLoop </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">CFRunLoopRef runLoop &#x3D; CFRunLoopGetCurrent();</span><br><span class="line">NSArray *allModes &#x3D; CFBridgingRelease(CFRunLoopCopyAllModes(runLoop)); </span><br><span class="line">while (1) &#123; </span><br><span class="line">    for (NSString *mode in allModes) &#123; </span><br><span class="line">        CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop让tableview延迟加载图片"><a href="#runloop让tableview延迟加载图片" class="headerlink" title="runloop让tableview延迟加载图片"></a>runloop让tableview延迟加载图片</h3><ul>
<li>以前加载都是在 cellforindexpathrow 加载，现在的思路是</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">UIImage *downloadedImage &#x3D; ...; </span><br><span class="line">[self.avatarImageView performSelector:@selector(setImage:) withObject:downloadedImage </span><br><span class="line">afterDelay:0 </span><br><span class="line">inModes:@[NSDefaultRunLoopMode]];</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<hr>
<ul>
<li>setupJJPerformselecterOne tableview延迟加载图片</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">- (void)setupJJPerformselecterOne </span><br><span class="line">&#123; </span><br><span class="line">    NSThread *thread &#x3D; [[NSThread alloc] initWithTarget:self selector:@selector(taskOne) object:nil]; </span><br><span class="line">    [thread start]; </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line">-(void)taskOne </span><br><span class="line">&#123; </span><br><span class="line">    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];&#x2F;&#x2F; 直到 1 秒后执行 </span><br><span class="line">    NSLog(@&quot;taskOne is running&quot;); </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop子线程定时任务"><a href="#runloop子线程定时任务" class="headerlink" title="runloop子线程定时任务"></a>runloop子线程定时任务</h3><ul>
<li>// 只执行 4 次， 1 秒执行一次 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">@property(nonatomic,strong)NSTimer *timer; </span><br><span class="line"></span><br><span class="line">- (void)handleSomeTimers:(float)startTime interval:(float)interval </span><br><span class="line">&#123; </span><br><span class="line">    dispatch_async(dispatch_get_global_queue(0, 0), ^&#123; </span><br><span class="line">        self.timer &#x3D; [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:startTime] </span><br><span class="line">        interval:interval </span><br><span class="line">        target:self </span><br><span class="line">        selector:@selector(runTimerWithRepeatCount) </span><br><span class="line">        userInfo:nil </span><br><span class="line">        repeats:YES]; </span><br><span class="line"></span><br><span class="line">        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; </span><br><span class="line">        [[NSRunLoop currentRunLoop] run]; </span><br><span class="line">    &#125;); </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line">- (void)runTimerWithRepeatCount&#123;</span><br><span class="line">    count ++; </span><br><span class="line">    if (count &#x3D;&#x3D; 4) &#123; </span><br><span class="line">        [self.timer invalidate]; </span><br><span class="line">    &#125; </span><br><span class="line">    NSLog(@&quot;runTimer__on thread %@&quot;,[NSThread currentThread]); </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop实现子线程常驻"><a href="#runloop实现子线程常驻" class="headerlink" title="runloop实现子线程常驻"></a>runloop实现子线程常驻</h3><ul>
<li>runloop 运行循环 runloop 应用场景【重要】【配合 NSTimer 】【子线程常驻】（后台常驻运行 runTimer 方法；每个 0.1 秒执行一次方法；）</li>
<li>子线程常驻的作用是，在 app 启动后，有一个线程在后面一直常驻，可以让这个线程定时去处理事情</li>
<li>setupJJTimerRunLoop 子线程：后台常驻运行 runTimer 方法；每个 0.01 秒执行一次方法； </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">@property(nonatomic,strong)NSTimer *timer; </span><br><span class="line"></span><br><span class="line">- (void)setupJJTimerRunLoop </span><br><span class="line">&#123; </span><br><span class="line">    dispatch_async(dispatch_get_global_queue(0, 0), ^&#123; </span><br><span class="line">        NSTimer* timer &#x3D; [[NSTimer alloc] initWithFireDate:[NSDate                 dateWithTimeIntervalSinceNow:0.01] </span><br><span class="line">    interval:0.01 </span><br><span class="line">    target:self </span><br><span class="line">    selector:@selector(runTimer) </span><br><span class="line">    userInfo:nil </span><br><span class="line">    repeats:YES]; </span><br><span class="line"></span><br><span class="line">        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; </span><br><span class="line">        [[NSRunLoop currentRunLoop] run];&#x2F;&#x2F; 默认不开，所以这里要手动打开 </span><br><span class="line">    &#125;); </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)runTimer </span><br><span class="line">&#123; </span><br><span class="line">    NSLog(@&quot;runTimer__on thread %@&quot;,[NSThread currentThread]); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="runloop在AFN中使用"><a href="#runloop在AFN中使用" class="headerlink" title="runloop在AFN中使用"></a>runloop在AFN中使用</h3><ul>
<li>【总结： AFN 的使用 runloop 的思路是，先创建一个线程，并把这个线程加入到 runloop 中，在 runloop 监听 port 端口，触发操作】</li>
<li>这是创建一个常驻服务线程的很好方法 </li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">+ (void)networkRequestThreadEntryPoint:(id)__unused object &#123; </span><br><span class="line">    @autoreleasepool &#123; </span><br><span class="line">        [[NSThread currentThread] setName:@&quot;AFNetworking&quot;]; </span><br><span class="line">        NSRunLoop *runLoop &#x3D; [NSRunLoop currentRunLoop]; </span><br><span class="line">        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];&#x2F;&#x2F; 开启循环，监听 port ，触发操作 </span><br><span class="line">        [runLoop run]; </span><br><span class="line">    &#125; </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line">+ (NSThread *)networkRequestThread &#123; </span><br><span class="line">    static NSThread *_networkRequestThread &#x3D; nil;&#x2F;&#x2F; 创建一个线程 </span><br><span class="line">    static dispatch_once_t oncePredicate; </span><br><span class="line">    dispatch_once(&amp;oncePredicate, ^&#123; </span><br><span class="line">        _networkRequestThread &#x3D; </span><br><span class="line">        [[NSThread alloc] initWithTarget:self </span><br><span class="line">    selector:@selector(networkRequestThreadEntryPoint:) </span><br><span class="line">    object:nil]; </span><br><span class="line">        [_networkRequestThread start]; </span><br><span class="line">    &#125;); </span><br><span class="line"></span><br><span class="line">    return _networkRequestThread; </span><br><span class="line">&#125; </span><br><span class="line"></span><br></pre></td></tr></table></figure>
    </div>

    
    
    

      <footer class="post-footer">
          <div class="post-tags">
              <a href="/blog/tags/%E5%BA%95%E5%B1%82/" rel="tag"># 底层</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/blog/2020/09/14/%E7%A7%BB%E5%8A%A8APP%E6%B7%B7%E5%90%88%E6%A1%86%E6%9E%B6/" rel="prev" title="移动APP混合框架">
      <i class="fa fa-chevron-left"></i> 移动APP混合框架
    </a></div>
      <div class="post-nav-item">
    <a href="/blog/2020/09/16/%E6%B5%8B%E8%AF%95/" rel="next" title="测试">
      测试 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Runloop%E5%AD%98%E5%9C%A8%E7%9A%84%E4%BB%B7%E5%80%BC"><span class="nav-number">1.</span> <span class="nav-text">Runloop存在的价值</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Runloop%E4%B8%8E%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.1.</span> <span class="nav-text">Runloop与线程</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Runloop%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86"><span class="nav-number">2.</span> <span class="nav-text">Runloop的工作原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%A4%E8%AF%86Runloop"><span class="nav-number">2.1.</span> <span class="nav-text">认识Runloop</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E5%BE%AA%E7%8E%AF%E6%9C%AC%E8%B4%A8"><span class="nav-number">2.2.</span> <span class="nav-text">运行循环本质</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Runloop%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B"><span class="nav-number">2.3.</span> <span class="nav-text">Runloop工作流程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BE%93%E5%85%A5%E6%BA%90%EF%BC%88mx%EF%BC%9A%E6%B6%88%E6%81%AF%E3%80%81%E6%97%B6%E9%97%B4-%E8%A6%81%E5%81%9A%E7%9A%84%E4%BA%8B%EF%BC%89"><span class="nav-number">2.4.</span> <span class="nav-text">输入源（mx：消息、时间-要做的事）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#mode%E6%A8%A1%E5%BC%8F%EF%BC%88mx%EF%BC%9A%E6%8B%BF%E5%88%B0%E6%BA%90%E5%B0%B1%E7%BB%99%E4%BB%96%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%BC%8F%EF%BC%8C%E6%98%AF%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%E7%9A%84%E6%A8%A1%E5%BC%8F%EF%BC%89"><span class="nav-number">2.5.</span> <span class="nav-text">mode模式（mx：拿到源就给他配置一个模式，是如何工作的模式）</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#mode%E7%9A%84%E7%A7%8D%E7%B1%BB%EF%BC%9A"><span class="nav-number">2.5.1.</span> <span class="nav-text">mode的种类：</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#mode%E5%88%87%E6%8D%A2"><span class="nav-number">2.5.2.</span> <span class="nav-text">mode切换</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E8%AE%A9%E4%B8%80%E4%B8%AA%E4%BA%8B%E4%BB%B6%E5%90%8C%E6%97%B6%E5%9C%A8%E4%B8%A4%E4%B8%AA-mode-%E9%83%BD%E6%9C%89%E5%9B%9E%E8%B0%83%E6%96%B9%E6%B3%95"><span class="nav-number">2.5.3.</span> <span class="nav-text">让一个事件同时在两个 mode 都有回调方法</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E8%A7%82%E5%AF%9F%E8%80%85"><span class="nav-number">2.6.</span> <span class="nav-text">runloop观察者</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#RunLoop-%E8%A7%82%E5%AF%9F%E8%80%85%E4%BB%8B%E7%BB%8D"><span class="nav-number">2.6.1.</span> <span class="nav-text">RunLoop 观察者介绍</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E8%A7%82%E5%AF%9F%E8%80%85%E7%8A%B6%E6%80%81"><span class="nav-number">2.6.2.</span> <span class="nav-text">观察者状态</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90"><span class="nav-number">2.6.3.</span> <span class="nav-text">参数解析</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%87%8A%E6%94%BE%E8%A7%82%E5%AF%9F%E8%80%85"><span class="nav-number">2.6.4.</span> <span class="nav-text">释放观察者</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%B7%BB%E5%8A%A0%E8%A7%82%E5%AF%9F%E8%80%85%E8%BF%87%E7%A8%8B"><span class="nav-number">2.6.5.</span> <span class="nav-text">添加观察者过程</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E5%90%AF%E5%8A%A8%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%EF%BC%9A"><span class="nav-number">2.7.</span> <span class="nav-text">runloop启动的三种方式：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BB%93%E6%9D%9F%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%EF%BC%9A"><span class="nav-number">2.8.</span> <span class="nav-text">结束的三种方式：</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#runloop%E4%BD%BF%E7%94%A8%E7%9A%84%E4%BE%8B%E5%AD%90"><span class="nav-number">3.</span> <span class="nav-text">runloop使用的例子</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E8%A7%A3%E5%86%B3cell%E6%BB%91%E5%8A%A8timer%E5%81%9C%E6%AD%A2"><span class="nav-number">3.1.</span> <span class="nav-text">runloop解决cell滑动timer停止</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E8%AE%A9crash%E7%9A%84app%E8%B5%B7%E6%AD%BB%E5%9B%9E%E7%94%9F"><span class="nav-number">3.2.</span> <span class="nav-text">runloop让crash的app起死回生</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E8%AE%A9tableview%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD%E5%9B%BE%E7%89%87"><span class="nav-number">3.3.</span> <span class="nav-text">runloop让tableview延迟加载图片</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1"><span class="nav-number">3.4.</span> <span class="nav-text">runloop子线程定时任务</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E5%AE%9E%E7%8E%B0%E5%AD%90%E7%BA%BF%E7%A8%8B%E5%B8%B8%E9%A9%BB"><span class="nav-number">3.5.</span> <span class="nav-text">runloop实现子线程常驻</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#runloop%E5%9C%A8AFN%E4%B8%AD%E4%BD%BF%E7%94%A8"><span class="nav-number">3.6.</span> <span class="nav-text">runloop在AFN中使用</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="汶"
      src="/blog/images/side_image.png">
  <p class="site-author-name" itemprop="name">汶</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blog/archives/">
        
          <span class="site-state-item-count">6</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">分类</span>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/codeRiding" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;codeRiding" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/coderiding" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;coderiding" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">汶</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/blog/lib/anime.min.js"></script>
  <script src="/blog/lib/velocity/velocity.min.js"></script>
  <script src="/blog/lib/velocity/velocity.ui.min.js"></script>

<script src="/blog/js/utils.js"></script>

<script src="/blog/js/motion.js"></script>


<script src="/blog/js/schemes/pisces.js"></script>


<script src="/blog/js/next-boot.js"></script>




  















  

  

</body>
</html>
